﻿using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

namespace Narwhunderful.Aphelia.Framework.IO.Serialization.Binary
{

    public sealed class SizeOptimizedBinaryReader : IBinaryReader
    {
        // .NET 2.0 doesn't support Func, it has to be manually declared
        private delegate T HeaderCallback<T>(string text);

        private readonly Encoding _encoding;
        private readonly IList<string> _names = new List<string>();
        private readonly ITypeNameConverter _typeNameConverter;

        // Translation table of types
        private readonly IList<Type> _types = new List<Type>();
        private BinaryReader _reader;

        // Translation table of property names
        public SizeOptimizedBinaryReader(ITypeNameConverter typeNameConverter, Encoding encoding)
        {
            if (typeNameConverter == null) throw new ArgumentNullException("typeNameConverter");
            if (encoding == null) throw new ArgumentNullException("encoding");
            _typeNameConverter = typeNameConverter;
            _encoding = encoding;
        }

        public byte ReadElementId()
        {
            return _reader.ReadByte();
        }

        public Type ReadType()
        {
            int index = BinaryReaderHelpers.ReadNumber(_reader);
            return _types[index];
        }

        public int ReadNumber()
        {
            return BinaryReaderHelpers.ReadNumber(_reader);
        }

        public int[] ReadNumbers()
        {
            return BinaryReaderHelpers.ReadNumbers(_reader);
        }

        public string ReadName()
        {
            int index = BinaryReaderHelpers.ReadNumber(_reader);
            return _names[index];
        }

        public object ReadValue(Type expectedType)
        {
            return BinaryReaderHelpers.ReadValue(expectedType, _reader);
        }

        public void Open(Stream stream)
        {
            _reader = new BinaryReader(stream, _encoding);

            // read names
            ReadHeader(_reader, _names, text => text);

            // read types
            ReadHeader(_reader, _types, _typeNameConverter.ConvertToType);
        }

        public void Close()
        {
            // nothing to do
        }

        private static void ReadHeader<T>(BinaryReader reader, IList<T> items, HeaderCallback<T> readCallback)
        {
            // Count
            int count = BinaryReaderHelpers.ReadNumber(reader);

            // Items)
            for (int i = 0; i < count; i++)
            {
                string itemAsText = BinaryReaderHelpers.ReadString(reader);
                T item = readCallback(itemAsText);
                items.Add(item);
            }

        }
    }
}
